大家好,今天的篇章要介紹的是 React Intl
會延續前一天的專案結構接續下去進行修改,如果沒有參與到昨天的建置過程,這邊也有提供原始碼
https://github.com/littlehorseboy/typescript-react/tree/day15-formik-yup-material-ui
i18n 函式庫,可以格式化日期,數字和字符串,複數形式但這些都不會講到,筆者想要做的是多語系的功能,不過最近在爬文的過程中都發現一些文章的使用方式在官方載下來的函式庫中似乎已經不見了!難道版本更新過後舊的都不能用了!?
後來筆者就發現了這個 https://github.com/formatjs/react-intl/blob/master/examples/Hooks.tsx ,!! 居然有提供 Hooks 的使用方式,就直接參考這個來動手實作自己的多語系,筆者是用自己的方式實作的,所以應該會跟真正的 best practice 有些差距 XD,在輸出文字方面就還是會依靠這個 Hook useIntl
,先安裝 react-intl
npm i react-intl
接下來要新增 custom hook,這隻純粹是為了將一些程式碼拆分,不要擠到 Root.tsx 裡面而做的
+ src/useNavigatorLanguage.ts
第 5 行,typescript 的強型別定義可以使用的語系的 key 值
第 8 行,後面會用到下拉選單來切換 locale
第 13 行,createContext 準備放 component tree 能共用的 locale state
第 17 行,將 state 包裝成 custom hook 給 Root.tsx 用
+ src/useGetMessage.ts
這隻 custom hook 用來單獨放 message,並用 useEffect 假裝可以從 ajax 取回數據的感覺
然後就塞 Provider 給 Root.tsx 囉
紅色底線標示的地方為本次新增的程式碼,react-intl 的 IntlProvider 外層還多包了一層 createContext 的 Provider,value 放前面做的 state
然後新增一個可以開下拉選單來切換 locale 的 Button,因為不要讓 Header.tsx 增加太多程式碼,也是以新增一個 tsx 的方式來放
+ src/components/Header/LanguageButton/LanguageButton.tsx
這裡是用了 material-ui 的 Menus 來改造而成的,比較需要注意的應該是 const [locale, setLocale] = useContext(LocaleContext)
這段取回了前面 createContext 傳進的 state,可以直接使用 setLocale
來改變根組件上的 locale
然後將它放進 Header.tsx
最後就是用 useIntl
來輸入最之前定義的 messages 了,筆者這邊改動 Home.tsx 來示範
再回顧一次剛剛 useGetMessage.ts 定義了什麼
還有 Root.tsx 是如何引入 messages
可以看出 useIntl 所解構出的 formatMessage
,直接用 id 來對應 messages 的內容即可
執行結果
初次載入時會顯示 defaultMessage,然後假裝 ajax 取回數據就會套回中文,後續就是單純切換改變內容
以上就是筆者在使用 React Intl 自己實現多語系的一點小小心得,然後,原來是筆者耍笨不好好看官方文件,後來才發現 github 上這個 Breaking API Changes,像是在說有一個 Intl.PluralRules
可以達成我想要做的事情,不過現在突然要研究起來會來不及發文 XD,只好留待日後有機會再研究看看
最後附上原始碼
https://github.com/littlehorseboy/typescript-react/tree/day16-react-intl-useintl-usecontext
明天要介紹 Draft.js,將 HTML contenteditable 用 React 的 state 來控制的 rich text editors(翻譯會是富文本編輯器,這個會是正確的名稱嗎? XD)